//
//  NSObject+KVO.m
//  TestDemo
//
//  Created by apple on 2022/1/15.
//

#import "NSObject+KVO.h"
#import <objc/runtime.h>
#import <objc/message.h>

@interface NSKVOAssociation : NSObject

@property(nonatomic,weak)NSObject * addObserver;

@property(nonatomic,weak)NSObject *observer;

@property(nonatomic,copy)NSString * keyPath;

@property(nonatomic,assign)NSKeyValueObservingOptions options;

@property(nonatomic)void * context;

@end

@implementation NSKVOAssociation


@end



@implementation NSObject (KVO)
- (void)lg_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context{
    
    //注册类
    NSString * oldClassName = NSStringFromClass([self class]);
    NSString * newClassName = [NSString stringWithFormat:@"NSKVONotifying_%@",oldClassName];
    
    Class KVOClass =NSClassFromString(newClassName);
    
    if (!KVOClass) {
        KVOClass = objc_allocateClassPair([self class], [newClassName UTF8String], 0);
        objc_registerClassPair(KVOClass);
    }
    //重写setter方法
    NSString * setterMethodN =[NSString stringWithFormat:@"set%@:",[keyPath capitalizedString]];
    SEL sel =NSSelectorFromString(setterMethodN);
    
    const char * types = [self methodEncoding:setterMethodN];
    class_addMethod(KVOClass, sel, (IMP)_lgNSSetObjectValueAndNotify,types);
    
    //修改对象的ISA指针
    object_setClass(self, KVOClass);
    
    //存储target 和keyPath
    [self printMethodList:KVOClass];
    
    //保存observer 和 keyPath数据
   NSMutableDictionary * dictonary = objc_getAssociatedObject(self,class_getName([self class]));
    if (!dictonary) {
        dictonary = [NSMutableDictionary dictionary];
    }
    NSMutableArray * observers =  [dictonary objectForKey:keyPath];
    if (!observers) {
        observers = [NSMutableArray array];
    }
    
    NSKVOAssociation * association = [[NSKVOAssociation alloc]init];
    association.observer = observer;
    association.keyPath = keyPath;
    association.options =options;
    association.context =context;
    association.addObserver = self;
    [observers addObject:association];
    [dictonary setValue:observers forKey:keyPath];
    objc_setAssociatedObject(self, class_getName([self class]), dictonary, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
}


-(const char *)methodEncoding:(NSString *)methodName{
    unsigned int count;
    Method * methods = class_copyMethodList([self class], &count);
    for(unsigned int i = 0; i < count; i++) {
        
        Method method = methods[i];
        if ([methodName isEqualToString: NSStringFromSelector(method_getName(method))]) {
            return  method_getTypeEncoding(method);
        }
    }
    return NULL;
}

static NSString *getterForSetter(NSString *setter){
    
    if (setter.length <= 0 || ![setter hasPrefix:@"set"] || ![setter hasSuffix:@":"]) { return nil;}
    
    NSRange range = NSMakeRange(3, setter.length-4);
    NSString *getter = [setter substringWithRange:range];
    NSString *firstString = [[getter substringToIndex:1] lowercaseString];
    return  [getter stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:firstString];
}

void _lgNSSetObjectValueAndNotify(id self,SEL _cmd,id value){
    
    NSString * keypath =getterForSetter(NSStringFromSelector(_cmd));
    //调用willChangeValueForKey方法
    [self willChangeValueForKey:keypath];
    
    id receiver = self;
    //向父类发送setter方法 修改值
    Class super_class = class_getSuperclass([self class]);
    
    struct objc_super objc_super = {
        .receiver = receiver,
        .super_class =super_class
    };
    int (*msgSendSuper)(void *, SEL, id) = (void *)objc_msgSendSuper;
    
    msgSendSuper(&objc_super,_cmd,value);
    //调用 didChangeValueForKey
    [self didChangeValueForKey:keypath];
    
    //发送通知
    NSMutableDictionary * dictonary = objc_getAssociatedObject(self,class_getName([self class]));
    NSMutableArray * observers =  [dictonary objectForKey:keypath];
    [observers enumerateObjectsUsingBlock:^(NSKVOAssociation * obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [obj.observer observeValueForKeyPath:obj.keyPath ofObject:obj.addObserver change:@{NSKeyValueChangeKindKey:value} context:obj.context];
    }];
}

#pragma mark 打印方法
-(void)printMethodList:(Class)class{
    unsigned int count;
    Method * methods = class_copyMethodList(class, &count);
    for ( unsigned int i=0; i<count; i++) {
        Method method = methods[i];
        NSLog(@"class:%@----method_name:---%@",NSStringFromClass(class),NSStringFromSelector(method_getName(method)));
    }
    
}

- (void)lg_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath{
    //修改ISA指针
    object_setClass(self, class_getSuperclass([self class]));
}

@end
